# -*- coding: utf-8 -*-
# Purpose: 
# Created: 11/20/2008
# Copyright (c) 2008,2009 by University College London
# Authors:
# Didrik Pinte <dpinte@dipole-consulting.com>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with HydroPlatform for details.
        
import logging
import sys
import unittest
import wx

import Thuban
import Thuban.UI.baserenderer
from hydroplatform.model.layer import NetworkLayer
from hydroplatform.model.network import get_thirdpoint

def render_network_layer(renderer, layer):
    
    for link in layer.network.links:
        # for each link :
        #  - project nodes 
        #  - draw nodes
        #  - draw link
        try:
            project_link(renderer, layer, link)
            # TODO :this could be improved by not recreating all the types 
            # all the time ...
            if link.isSelected():
                pen = wx.Pen(u"black", width=3)
            else: 
                pen = wx.Pen(link.type.pen, 
                               width=link.type.size,
                               style=eval(link.type.style))
            draw_link(renderer.dc, 
                      link.screenpoints,
                      pen, wx.Brush(u"black"))
        except Exception,e:
            logging.error(e)
            continue
    
    for node in layer.network.nodes:
        # reproject node to screen
        try:
            project_node(renderer, layer, node)
        except Exception, e:
            logging.error("Error while projecting node %s with error %s" % \
                          (node, e)) 
            continue
        # paint the node according to it's type
        if node.isSelected() is True:
            node.color = "red"
        else :
            node.color = node.type.pen
          
        # not using the resolution from the renderer because the symbols are
        # pixel based !
        symbols[node.type.shape](renderer.dc, node.screenpoint, node.type.size,
                                 wx.Pen(node.color), wx.Brush(node.type.brush)
                                 )
        
        if layer.labeled is True:
            dc = renderer.dc
            dc.DrawText(node.name, node.screenpoint.x -5, node.screenpoint.y)
            
    return ()

# FIXME : using kwargs should be cleaner than lots of parameters ...

def draw_triangle(dc, point, size, 
                  pen=wx.BLACK_PEN, brush=wx.TRANSPARENT_BRUSH, 
                  resolution=1.0):
    dc.SetPen(pen)
    dc.SetBrush(brush)
    # the size of the triangle must match the output size, thus, removing 
    # one pixel from left and right, and divinding by two should center it
    size = int(round(resolution * (size - 2))) / 2.0
    # draw a triangle
    points = [wx.Point(point[0], point[1] + size),
              wx.Point(point[0] + size, point[1] - size),
              wx.Point(point[0] - size, point[1] - size)
            ]
    dc.DrawPolygon(points)

def draw_circle(dc, point, size, 
                pen=wx.BLACK_PEN, brush=wx.TRANSPARENT_BRUSH, 
                resolution=1.0):
    dc.SetBrush(brush)
    dc.SetPen(pen)
    radius = int(round(resolution * (size-2) /2.0))
    dc.DrawCirclePoint(point, radius)
    
def draw_ellipse(dc, point, size, 
                 pen=wx.BLACK_PEN, brush=wx.TRANSPARENT_BRUSH, 
                 resolution=1.0):
    dc.SetBrush(brush)
    dc.SetPen(pen)
    xradius = int(round(resolution * size/2.0))
    yradius = int(round(resolution * (size/4.0)))
    dc.DrawEllipse(point[0] - xradius, point[1] - yradius,
                        2 * xradius, 2 * yradius)    

def draw_rectangle(dc, point, size, 
                   pen=wx.BLACK_PEN, brush=wx.TRANSPARENT_BRUSH, 
                   resolution=1.0):
    _draw_rectangle(dc, point, size, size/2.0, pen, brush, resolution)
    
def draw_square(dc, point, size, 
                pen=wx.BLACK_PEN, brush=wx.TRANSPARENT_BRUSH, 
                resolution=1.0):
    _draw_rectangle(dc, point, size, size, pen, brush, resolution)

def _draw_rectangle(dc, point, width, height, pen, brush, resolution=1.0):
    '''
    Draws a rectangle centered on the given point !
    ''' 
    assert isinstance(dc, wx.DC)
    dc.SetBrush(brush)
    dc.SetPen(pen)
    dc.DrawRectangle(point[0]-(width/2.0), point[1]-(height/2.0), 
                     width, height)

def draw_link(dc, points, 
              pen=wx.BLACK_PEN, brush=wx.TRANSPARENT_BRUSH, 
              resolution=1.0):
    assert isinstance(dc, wx.DC)
    dc.SetBrush(brush)
    dc.SetPen(pen)
    if len(points) < 3: 
        # assumes it is two wx.Point in a list
        dc.DrawLinePoint(*points)
    else :
        dc.DrawSpline(points)
    
def draw_line(dc, point, size, 
                   pen=wx.BLACK_PEN, brush=None, 
                   resolution=1.0):
    '''
    Draw a symbo representing a line with point as the central point
    '''
    
    llpt = wx.Point(point[0] - size/2.0, point[1] - size/2.0)
    mpt = wx.Point(point[0] + size/4.0,  point[1] - size/4.0)
    cpt = wx.Point(point[0], point[1])
    urpt = wx.Point(point[0] + size/2.0, point[1] + size/2.0)
    points = [llpt, mpt, cpt, urpt] 
    assert isinstance(dc, wx.DC)
    dc.SetPen(pen)
    dc.DrawLines(points)

def project_node(renderer, layer, node):
    '''
    Project node coordinates to pixel coordinates
    according to the renderer configuration
    '''
    points = renderer.projected_points(layer, [[(node.x, node.y)]])
    node.screenpoint = points[0][0]

def project_link(renderer, layer, link):
    '''
    Project link coordinates to pixel coordinates according 
    to the renderer configuration
    '''    
    coords = link.getGeometry( \
        layer.network.has_link(link.end, link.start)).coords
    points = renderer.projected_points(layer, [coords])
    link.screenpoints = points[0]
    
def getBitmapForNodeType(nodetype, size, background):
    '''
    Returns a wx.Bitmap object for the given 
    
    @parameter nodetype hydroplatform.model.network.NodeType
    @return wx.Bitmap
    '''
    
    return getBitmapForShape(nodetype.shape, 
                             size, 
                             wx.Pen(nodetype.pen), 
                             wx.Brush(nodetype.brush),
                             background)

def getBitmapForShape(shape_name, size, 
                      pen=wx.BLACK_PEN, brush=wx.WHITE_BRUSH, 
                      background=wx.WHITE_BRUSH):
    '''
    Return a wx.Bitmap object for the given shape name. 
    The shape is included in a square with a size=size.
    '''
    if symbols.has_key(shape_name):
        bmp = wx.EmptyBitmap(size, size)
        dc = wx.MemoryDC()
        dc.SelectObject(bmp)
        dc.SetBackground(background)
        dc.Clear()
        symbols[shape_name](dc, [size/2, size/2], size, pen, brush)
        return bmp
    else:
        raise NotImplementedError("There is no definition for the shape %s" % \
                                  shape_name)

Thuban.UI.baserenderer.add_renderer_extension(NetworkLayer, 
                                              render_network_layer)

symbols = {'triangle' :  draw_triangle,
           'circle' :    draw_circle,
           'ellipse' :   draw_ellipse,
           'rectangle' : draw_rectangle,
           'square' :    draw_square,
           'line'      : draw_line
           }